--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit d7b66171bf82b3e6501cc71c1edfaa03e365f0ff
Parents : 9c913bd
Author : Ivan <ivan@quad4.io>
Signature : Invalid signer <e46112d44649266d71fe2193e00a4710>, author is <ivan@quad4.io>
Date : 2026-07-16T17:31:07-05:00
feat: update backend benchmarks with new conversation handling and missed call metrics
Changes
5 files changed, 242 insertions(+), 8 deletions(-)
Diff
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40c534b9..19213449 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,8 @@ All notable changes to this project will be documented in this file.
- Relay Chat: denser hub UI, announce interval, collapsed system lines, reconnect notices
- Low-memory cleanup and SQLite pragmas under memory pressure
- CI benches use median-of-medians and quieter regression gates
+- Backend benchmarks cover slim conversation list, mark-as-read, call history, and missed-call notification paths
+- Benchmark gate fails when a full-suite run drops required benches or loses coverage vs baseline
- Backend tests can run sharded in CI
- Plugin strings live in plugin bundles, not main locale files
- Docker frontend build installs Go, builds visualiser WASM, and fails if WASM artifacts are missing
diff --git a/meshchatx.rsm b/meshchatx.rsm
index f2b16eca..8952f9b8 100644
Binary files a/meshchatx.rsm and b/meshchatx.rsm differ
diff --git a/tests/backend/compare_benchmarks.py b/tests/backend/compare_benchmarks.py
index 9c188ace..a265757c 100644
--- a/tests/backend/compare_benchmarks.py
+++ b/tests/backend/compare_benchmarks.py
@@ -29,6 +29,32 @@ from tests.backend.benchmarking_utils import ( # noqa: E402
should_alert_regression,
)
+# Must stay present in comprehensive suite runs. Gate fails if a full-suite
+# baseline is missing any of these from the current JSON.
+REQUIRED_BENCHES = frozenset(
+ {
+ "Database Initialization",
+ "Message Upsert (Batch of 100)",
+ "Get 100 Conversations List",
+ "Get Conversations Slim List (handler)",
+ "Get Conversations Unread Filter (handler)",
+ "Mark Conversation As Read",
+ "Get Messages for Conversation (offset 500)",
+ "Log Telephone Call",
+ "Get Call History List",
+ "Notification Add + Unread Count",
+ "Missed Call Unread Count by Type",
+ "Dismiss Missed Call Notifications",
+ "Config Get (50 keys)",
+ "Get Contacts List",
+ },
+)
+
+
+def _is_full_suite_baseline(previous):
+ """True when previous looks like the comprehensive suite, not a unit fixture."""
+ return "Database Initialization" in previous
+
def _load_entries(path):
with open(path, encoding="utf-8") as f:
@@ -91,6 +117,8 @@ def compare(
alerts = []
improvements = []
skipped = []
+ coverage_alerts = []
+ enforce_coverage = _is_full_suite_baseline(previous)
for name in sorted(current):
cur = current[name]
@@ -153,23 +181,55 @@ def compare(
)
missing = sorted(set(previous) - set(current))
+ coverage_seen = set()
for name in missing:
+ detail = "present in baseline only"
+ status = "removed"
+ if enforce_coverage:
+ coverage_alerts.append(name)
+ coverage_seen.add(name)
+ if name in REQUIRED_BENCHES:
+ status = "MISSING"
+ detail = "required bench absent from current suite"
+ else:
+ status = "REMOVED"
+ detail = "dropped from suite vs full baseline"
rows.append(
{
"name": name,
"current": None,
"previous": _value_ms(previous[name]),
"ratio": None,
- "status": "removed",
- "detail": "present in baseline only",
+ "status": status,
+ "detail": detail,
},
)
+ check_required = enforce_coverage or (
+ not previous and "Database Initialization" in current
+ )
+ if check_required:
+ for name in sorted(REQUIRED_BENCHES - set(current) - coverage_seen):
+ coverage_alerts.append(name)
+ rows.append(
+ {
+ "name": name,
+ "current": None,
+ "previous": (
+ _value_ms(previous[name]) if name in previous else None
+ ),
+ "ratio": None,
+ "status": "MISSING",
+ "detail": "required bench absent from current suite",
+ },
+ )
+
lines = [
"MeshChatX Backend Benchmark Gate",
f"Current: {current_path}",
f"Previous: {previous_path or '(none)'}",
f"Noise floor: {noise_floor_ms} ms | Min abs delta: {min_abs_delta_ms} ms",
+ f"Coverage enforce: {'yes' if check_required else 'no'}",
"",
f"{'Benchmark':42} {'Curr':>10} {'Prev':>10} {'Ratio':>8} Status",
"-" * 90,
@@ -182,17 +242,23 @@ def compare(
f"{row['name'][:42]:42} {cur_s:>10} {prev_s:>10} {ratio_s:>8} "
f"{row['status']}",
)
- if row["status"] == "REGRESSION":
+ if row["status"] in {"REGRESSION", "REMOVED", "MISSING"}:
lines.append(f" -> {row['detail']}")
lines.append("-" * 90)
lines.append(
f"Regressions: {len(alerts)} | Improvements: {len(improvements)} | "
f"Noise-skipped: {len(skipped)} | New: "
- f"{sum(1 for r in rows if r['status'] == 'new')}",
+ f"{sum(1 for r in rows if r['status'] == 'new')} | "
+ f"Coverage: {len(coverage_alerts)}",
)
- if alerts:
- lines.append("ALERT: " + ", ".join(alerts))
+ if alerts or coverage_alerts:
+ parts = []
+ if alerts:
+ parts.append("regressions: " + ", ".join(alerts))
+ if coverage_alerts:
+ parts.append("coverage: " + ", ".join(coverage_alerts))
+ lines.append("ALERT: " + " | ".join(parts))
else:
lines.append("No actionable regressions.")
@@ -210,7 +276,7 @@ def compare(
f.write(text)
f.write("```\n")
- return 1 if alerts else 0, rows
+ return 1 if (alerts or coverage_alerts) else 0, rows
def update_baseline(
diff --git a/tests/backend/run_comprehensive_benchmarks.py b/tests/backend/run_comprehensive_benchmarks.py
index 64565843..41f165fb 100644
--- a/tests/backend/run_comprehensive_benchmarks.py
+++ b/tests/backend/run_comprehensive_benchmarks.py
@@ -22,6 +22,7 @@ from meshchatx.src.backend.database.map_drawings import MapDrawingsDAO # noqa:
from meshchatx.src.backend.database.telephone import TelephoneDAO # noqa: E402
from meshchatx.src.backend.database.voicemails import VoicemailDAO # noqa: E402
from meshchatx.src.backend.identity_manager import IdentityManager # noqa: E402
+from meshchatx.src.backend.message_handler import MessageHandler # noqa: E402
from tests.backend.benchmarking_utils import ( # noqa: E402
BenchmarkResult,
aggregate_run_results,
@@ -259,6 +260,24 @@ class BackendBenchmarker:
def get_convs():
return self.db.messages.get_conversations()
+ @benchmark("Get Conversations Slim List (handler)", iterations=10)
+ def get_convs_handler():
+ handler = MessageHandler(self.db)
+ return handler.get_conversations(self.my_hash, limit=100)
+
+ @benchmark("Get Conversations Unread Filter (handler)", iterations=10)
+ def get_convs_unread():
+ handler = MessageHandler(self.db)
+ return handler.get_conversations(
+ self.my_hash,
+ filter_unread=True,
+ limit=100,
+ )
+
+ @benchmark("Mark Conversation As Read", iterations=20)
+ def mark_read():
+ self.db.messages.mark_conversation_as_read(random.choice(peer_hashes))
+
@benchmark("Get Messages for Conversation (offset 500)", iterations=20)
def get_messages():
return self.db.messages.get_conversation_messages(
@@ -276,7 +295,12 @@ class BackendBenchmarker:
_, res = get_convs()
self.results.append(res)
-
+ _, res = get_convs_handler()
+ self.results.append(res)
+ _, res = get_convs_unread()
+ self.results.append(res)
+ _, res = mark_read()
+ self.results.append(res)
_, res = get_messages()
self.results.append(res)
@@ -385,8 +409,24 @@ class BackendBenchmarker:
timestamp=time.time(),
)
+ @benchmark("Get Call History List", iterations=20)
+ def get_history():
+ return dao.get_call_history(limit=50)
+
+ for _ in range(40):
+ dao.add_call_history(
+ remote_identity_hash=secrets.token_hex(16),
+ remote_identity_name="Seed Peer",
+ is_incoming=True,
+ status="Busy",
+ duration_seconds=0,
+ timestamp=time.time(),
+ )
+
_, res = log_call()
self.results.append(res)
+ _, res = get_history()
+ self.results.append(res)
def bench_contact_operations(self):
dao = ContactsDAO(self.db.provider)
@@ -662,6 +702,27 @@ class BackendBenchmarker:
)
return self.db.misc.get_unread_notification_count()
+ @benchmark("Missed Call Unread Count by Type", iterations=20)
+ def missed_call_count():
+ return self.db.misc.get_unread_notification_count_by_type(
+ "telephone_missed_call",
+ )
+
+ @benchmark("Dismiss Missed Call Notifications", iterations=10)
+ def dismiss_missed_calls():
+ with self.db.provider:
+ for _ in range(5):
+ self.db.misc.add_notification(
+ notification_type="telephone_missed_call",
+ remote_hash=random.choice(dest_hashes),
+ title="Missed Call",
+ content="bench missed call",
+ )
+ self.db.misc.dismiss_unviewed_notifications("telephone_missed_call")
+ return self.db.misc.get_unread_notification_count_by_type(
+ "telephone_missed_call",
+ )
+
for _ in range(50):
with self.db.provider:
self.db.misc.add_blocked_destination(random.choice(dest_hashes))
@@ -672,6 +733,13 @@ class BackendBenchmarker:
"#fff",
"#000",
)
+ with self.db.provider:
+ self.db.misc.add_notification(
+ notification_type="telephone_missed_call",
+ remote_hash=random.choice(dest_hashes),
+ title="Missed Call",
+ content="seed missed call",
+ )
_, res = blocked_dest_roundtrip()
self.results.append(res)
@@ -683,6 +751,10 @@ class BackendBenchmarker:
self.results.append(res)
_, res = notification_roundtrip()
self.results.append(res)
+ _, res = missed_call_count()
+ self.results.append(res)
+ _, res = dismiss_missed_calls()
+ self.results.append(res)
def print_summary(self, json_output_path=None):
suite_runs = getattr(self, "_suite_runs", 1)
diff --git a/tests/backend/test_benchmark_gate.py b/tests/backend/test_benchmark_gate.py
index 983b1c1d..b22e1c99 100644
--- a/tests/backend/test_benchmark_gate.py
+++ b/tests/backend/test_benchmark_gate.py
@@ -233,6 +233,100 @@ class TestCompareBenchmarks(unittest.TestCase):
)
self.assertEqual(code, 1)
+ def test_full_suite_fails_when_required_bench_missing(self):
+ with tempfile.TemporaryDirectory() as tmp:
+ current = os.path.join(tmp, "current.json")
+ previous = os.path.join(tmp, "previous.json")
+ # Full-suite signal without the new required conversation benches.
+ self._write(
+ previous,
+ [
+ {
+ "name": "Database Initialization",
+ "unit": "ms",
+ "value": 20.0,
+ },
+ {
+ "name": "Get 100 Conversations List",
+ "unit": "ms",
+ "value": 5.0,
+ },
+ ],
+ )
+ self._write(
+ current,
+ [
+ {
+ "name": "Database Initialization",
+ "unit": "ms",
+ "value": 21.0,
+ },
+ {
+ "name": "Get 100 Conversations List",
+ "unit": "ms",
+ "value": 5.1,
+ },
+ ],
+ )
+ code, rows = compare(current, previous)
+ by_name = {r["name"]: r for r in rows}
+ self.assertEqual(code, 1)
+ self.assertEqual(
+ by_name["Get Conversations Slim List (handler)"]["status"],
+ "MISSING",
+ )
+
+ def test_full_suite_fails_when_bench_removed(self):
+ with tempfile.TemporaryDirectory() as tmp:
+ current = os.path.join(tmp, "current.json")
+ previous = os.path.join(tmp, "previous.json")
+ self._write(
+ previous,
+ [
+ {
+ "name": "Database Initialization",
+ "unit": "ms",
+ "value": 20.0,
+ },
+ {
+ "name": "Extra Optional Bench",
+ "unit": "ms",
+ "value": 1.0,
+ },
+ ],
+ )
+ # Current only has init: required benches missing + optional removed.
+ self._write(
+ current,
+ [
+ {
+ "name": "Database Initialization",
+ "unit": "ms",
+ "value": 20.0,
+ },
+ ],
+ )
+ code, rows = compare(current, previous)
+ by_name = {r["name"]: r for r in rows}
+ self.assertEqual(code, 1)
+ self.assertEqual(by_name["Extra Optional Bench"]["status"], "REMOVED")
+
+ def test_toy_fixture_does_not_enforce_required_coverage(self):
+ with tempfile.TemporaryDirectory() as tmp:
+ current = os.path.join(tmp, "current.json")
+ previous = os.path.join(tmp, "previous.json")
+ self._write(
+ current,
+ [{"name": "Toy Bench", "unit": "ms", "value": 1.0}],
+ )
+ self._write(
+ previous,
+ [{"name": "Toy Bench", "unit": "ms", "value": 1.0}],
+ )
+ code, rows = compare(current, previous)
+ self.assertEqual(code, 0)
+ self.assertEqual(rows[0]["status"], "ok")
+
if __name__ == "__main__":
unittest.main()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────